/*
 *  BGE_FUNC.cpp
 *  OSXBGE
 *
 *  Created by Stafford Brunk on 2/6/09.
 *  Copyright 2009 Northpole Software. All rights reserved.
 *
 *  Functions in this file are taken from the BGE driver in FreeBSD
 *	and modified accordingly
 */

#include "OSXBGE.h"
#include "BGEHW_REG.h"
#include "BGE_REG.h"


typedef IOPhysicalAddress dma_addr_t;

#pragma mark -
#pragma mark Ex-Macros
#pragma mark -

void OSXBGE::BGE_SETBIT(struct bge_softc *sc, uint32_t reg, uint32_t x)
{
	uint32_t val = CSR_READ_4(sc, reg) | x;
	CSR_WRITE_4(sc, reg, val);
}

void OSXBGE::BGE_CLRBIT(struct bge_softc *sc, uint32_t reg, uint32_t x)
{
	uint32_t val = CSR_READ_4(sc, reg) & (~x);
	CSR_WRITE_4(sc, reg, val);
}

void OSXBGE::PCI_SETBIT(UInt8 reg, UInt32 x)
{
	fPCIDevice->configWrite32(reg, (fPCIDevice->configRead32(reg) | (x)));
}

void OSXBGE::PCI_CLRBIT(UInt8 reg, UInt32 x)
{
	fPCIDevice->configWrite32(reg, (fPCIDevice->configRead32(reg) & ~(x)));
}

uint32_t OSXBGE::CSR_READ_4(struct bge_softc *sc, uint32_t reg)
{
	return OSReadLittleInt32(sc->bge_bhandle, reg);
}

void OSXBGE::CSR_WRITE_4(struct bge_softc *sc, uint32_t reg, uint32_t val)
{
	OSWriteLittleInt32(sc->bge_bhandle, reg, val);
}

UInt32 OSXBGE::BGE_MEMWIN_READ(UInt32 x)
{
	UInt32 val;
	
	do 
	{
		fPCIDevice->configWrite32(BGE_PCI_MEMWIN_BASEADDR, (0xFFFF0000 & x));
		val = CSR_READ_4(sc, BGE_MEMWIN_START + (x & 0xFFFF));
	} while(0);
	
	return val;
}

void OSXBGE::BGE_MEMWIN_WRITE(UInt32 x, UInt32 val)
{
	do
	{
		fPCIDevice->configWrite32(BGE_PCI_MEMWIN_BASEADDR, (0xFFFF0000 & x));
		CSR_WRITE_4(sc, BGE_MEMWIN_START + (x & 0xFFFF), val);
	} while(0);
}

#pragma mark -
#pragma mark Memory Read/Write
#pragma mark -

uint32_t OSXBGE::bge_readmem_ind(struct bge_softc *sc, int off)
{
	uint32_t val;
	
	//Special case for BCM5906 taken from the tg3 driver
	//if ((sc->bge_asicrev == BGE_ASICREV_BCM5906) && (off >= BGE_STATS_BLOCK) && (off < BGE_TX_RING_CNT))
	//		return 0;
	
	fPCIDevice->extendedConfigWrite32(BGE_PCI_MEMWIN_BASEADDR, off);
	val = fPCIDevice->extendedConfigRead32(BGE_PCI_MEMWIN_DATA);
	fPCIDevice->extendedConfigWrite32(BGE_PCI_MEMWIN_BASEADDR, 0);
	
	return val;
}

void OSXBGE::bge_writemem_ind(struct bge_softc *sc, int off, int val)
{
	//Special case for BCM5906 taken from the tg3 driver
	//if ((sc->bge_asicrev == BGE_ASICREV_BCM5906) && (off >= BGE_STATS_BLOCK) && (off < BGE_TX_RING_CNT))
		//return;
	
	fPCIDevice->extendedConfigWrite32(BGE_PCI_MEMWIN_BASEADDR, off);
	fPCIDevice->extendedConfigWrite32(BGE_PCI_MEMWIN_DATA, val);
	fPCIDevice->extendedConfigWrite32(BGE_PCI_MEMWIN_BASEADDR, 0);
}

uint32_t OSXBGE::bge_readreg_ind(struct bge_softc *sc, int off)
{
	fPCIDevice->configWrite32(BGE_PCI_REG_BASEADDR, off);
	return fPCIDevice->configRead32(BGE_PCI_REG_DATA);
}

void OSXBGE::bge_writereg_ind(struct bge_softc *sc, int off, int val)
{
	fPCIDevice->configWrite32(BGE_PCI_REG_BASEADDR, off);
	fPCIDevice->configWrite32(BGE_PCI_REG_DATA, val);
}

void OSXBGE::bge_writemem_direct(struct bge_softc *sc, int off, int val)
{
	CSR_WRITE_4(sc, off, val);
}

#pragma mark -
#pragma mark MII Functions
#pragma mark -

int OSXBGE::bge_miibus_readreg(struct bge_softc *sc, int phy, int reg)
{
	uint32_t val, autopoll;
	int i;
	
	/*
	 * Broadcom's own driver always assumes the internal
	 * PHY is at GMII address 1. On some chips, the PHY responds
	 * to accesses at all addresses, which could cause us to
	 * bogusly attach the PHY 32 times at probe type. Always
	 * restricting the lookup to address 1 is simpler than
	 * trying to figure out which chips revisions should be
	 * special-cased.
	 */
	if (phy != 1)
		return 0;
	
	/* Reading with autopolling on may trigger PCI errors */
	autopoll = CSR_READ_4(sc, BGE_MI_MODE);
	if (autopoll & BGE_MIMODE_AUTOPOLL)
	{
		BGE_CLRBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL);
		IODelay(40);
	}
	
	CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_READ | BGE_MICOMM_BUSY | BGE_MIPHY(phy) | BGE_MIREG(reg));
	
	for (i = 0; i < BGE_TIMEOUT; i++)
	{
		IODelay(10);
		val = CSR_READ_4(sc, BGE_MI_COMM);
		if (!(val & BGE_MICOMM_BUSY))
			break;
	}
	
	if (i == BGE_TIMEOUT)
	{
		DLOG ("PHY read timed out (phy %d, reg %d, val 0x%08x)\n", phy, reg, val);
		val = 0;
		goto done;
	}
	
	IODelay(5);
	val = CSR_READ_4(sc, BGE_MI_COMM);
	
done:
	if (autopoll & BGE_MIMODE_AUTOPOLL) 
	{
		BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL);
		IODelay(40);
	}
	
	if (val & BGE_MICOMM_READFAIL)
		return 0;
	
	return (val & 0xFFFF);
}

int OSXBGE::bge_miibus_writereg(struct bge_softc *sc, int phy, int reg, int val)
{
	uint32_t autopoll;
	int i;
	
	if (sc->bge_asicrev == BGE_ASICREV_BCM5906 && (reg == BRGPHY_MII_1000CTL || reg == BRGPHY_MII_AUXCTL))
		return 0;
	
	/* Reading with autopolling on may trigger PCI errors */
	autopoll = CSR_READ_4(sc, BGE_MI_MODE);
	if (autopoll & BGE_MIMODE_AUTOPOLL) 
	{
		BGE_CLRBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL);
		IODelay(40);
	}
	
	CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_WRITE | BGE_MICOMM_BUSY |
				BGE_MIPHY(phy) | BGE_MIREG(reg) | val);
	
	for (i = 0; i < BGE_TIMEOUT; i++) {
		IODelay(10);
		if (!(CSR_READ_4(sc, BGE_MI_COMM) & BGE_MICOMM_BUSY)) {
			IODelay(5);
			CSR_READ_4(sc, BGE_MI_COMM); /* dummy read */
			break;
		}
	}
	
	if (i == BGE_TIMEOUT) {
		DLOG("PHY write timed out (phy %d, reg %d, val %d)", phy, reg, val);
		return 0;
	}
	
	if (autopoll & BGE_MIMODE_AUTOPOLL) {
		BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL);
		IODelay(40);
	}
	
	return (0);
}

#pragma mark -
#pragma mark MAC Address Extraction
#pragma mark -

int OSXBGE::bge_get_eaddr_fw (struct bge_softc *sc, uint8_t ether_addr[])
{
	if (sc->bge_flags & BGE_FLAG_EADDR)
		return 1;
	
	return 1;
}

int OSXBGE::bge_get_eaddr_eeprom (struct bge_softc *sc, uint8_t ether_addr[])
{
	int mac_offset = BGE_EE_MAC_OFFSET;
	
	if (sc->bge_asicrev == BGE_ASICREV_BCM5906)
		return 1;
	
	return bge_read_eeprom(sc, (caddr_t) ether_addr, mac_offset + 2, ETHER_ADDR_LEN);
}

int OSXBGE::bge_get_eaddr_mem (struct bge_softc *sc, uint8_t ether_addr[])
{
	uint32_t mac_addr;
	
	mac_addr = bge_readmem_ind (sc, 0x0c14);
	
	if ((mac_addr >> 16) == 0x484b)
	{
		ether_addr[0] = (uint8_t) (mac_addr >> 8);
		ether_addr[1] = (uint8_t) mac_addr;
		mac_addr = bge_readmem_ind (sc, 0x0c18);
		ether_addr[2] = (uint8_t) (mac_addr >> 24);
		ether_addr[3] = (uint8_t) (mac_addr >> 16);
		ether_addr[4] = (uint8_t) (mac_addr >> 8);
		ether_addr[5] = (uint8_t) mac_addr;
		return 0;
	}
	
	return 1;
}

int OSXBGE::bge_get_eaddr_nvram(struct bge_softc *sc, uint8_t ether_addr[])
{
	int mac_offset = BGE_EE_MAC_OFFSET;
	
	if (sc->bge_asicrev == BGE_ASICREV_BCM5906)
		mac_offset = BGE_EE_MAC_OFFSET_5906;
	
	return (bge_read_nvram(sc, (caddr_t) ether_addr, mac_offset + 2, ETHER_ADDR_LEN));
}

int OSXBGE::bge_get_eaddr(struct bge_softc *sc, uint8_t eaddr[])
{
	if (bge_get_eaddr_fw (sc, eaddr) == 0)
	{
		DLOG("Got eaddr from fw");
		return 1;
	}
	else if (bge_get_eaddr_mem (sc, eaddr) == 0)
	{
		DLOG("Got eaddr from mem");
		return 1;
	}
	else if (bge_get_eaddr_nvram (sc, eaddr) == 0)
	{
		DLOG("Got eaddr from nvram");
		return 1;
	}
	else if (bge_get_eaddr_eeprom (sc, eaddr) == 0)
	{
		DLOG("Got eaddr from eeprom");
		return 1;
	}
	
	return 0;
}

uint8_t OSXBGE::bge_nvram_getbyte(struct bge_softc *sc, int addr, uint8_t *dest)
{
	uint32_t access, byte = 0;
	int i;
	
	/* Lock. */
	CSR_WRITE_4(sc, BGE_NVRAM_SWARB, BGE_NVRAMSWARB_SET1);
	for (i = 0; i < 8000; i++) {
		if (CSR_READ_4(sc, BGE_NVRAM_SWARB) & BGE_NVRAMSWARB_GNT1)
			break;
		IODelay(20);
	}
	if (i == 8000)
		return (1);
	
	/* Enable access. */
	access = CSR_READ_4(sc, BGE_NVRAM_ACCESS);
	CSR_WRITE_4(sc, BGE_NVRAM_ACCESS, access | BGE_NVRAMACC_ENABLE);
	
	CSR_WRITE_4(sc, BGE_NVRAM_ADDR, addr & 0xfffffffc);
	CSR_WRITE_4(sc, BGE_NVRAM_CMD, BGE_NVRAM_READCMD);
	for (i = 0; i < BGE_TIMEOUT * 10; i++) {
		IODelay(10);
		if (CSR_READ_4(sc, BGE_NVRAM_CMD) & BGE_NVRAMCMD_DONE) {
			IODelay(10);
			break;
		}
	}
	
	if (i == BGE_TIMEOUT * 10) {
		DLOG("nvram read timed out\n");
		return (1);
	}
	
	/* Get result. */
	byte = CSR_READ_4(sc, BGE_NVRAM_RDDATA);
	
	*dest = (CFSwapInt32(byte) >> ((addr % 4) * 8)) & 0xFF;
	
	/* Disable access. */
	CSR_WRITE_4(sc, BGE_NVRAM_ACCESS, access);
	
	/* Unlock. */
	CSR_WRITE_4(sc, BGE_NVRAM_SWARB, BGE_NVRAMSWARB_CLR1);
	CSR_READ_4(sc, BGE_NVRAM_SWARB);
	
	return (0);
}

/*
 * Read a sequence of bytes from NVRAM.
 */
int OSXBGE::bge_read_nvram(struct bge_softc *sc, caddr_t dest, int off, int cnt)
{
	int err = 0;
	uint8_t byte = 0;
	
	if (sc->bge_asicrev != BGE_ASICREV_BCM5906)
		return (1);
	
	for (int i = 0; i < cnt; i++) {
		err = bge_nvram_getbyte(sc, off + i, &byte);
		if (err)
			break;
		*(dest + i) = byte;
	}
	
	return (err ? 1 : 0);
}

int OSXBGE::bge_read_eeprom(struct bge_softc *sc, caddr_t dest, int off, int cnt)
{
	int i, error = 0;
	uint8_t byte = 0;
	
	for (i = 0; i < cnt; i++)
	{
		error = bge_eeprom_getbyte(sc, off + i, &byte);
		
		if (error)
			break;
		*(dest + i) = byte;
	}
	
	return (error ? 1 : 0);
}

uint8_t OSXBGE::bge_eeprom_getbyte(struct bge_softc *sc, int addr, uint8_t *dest)
{
	int i;
	uint32_t byte = 0;
	
	//Enable auto eeprom so we don't use bitbang
	BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_AUTO_EEPROM);
	
	//Reset EEPROM, load clock period
	CSR_WRITE_4(sc, BGE_EE_ADDR, BGE_EEADDR_RESET|BGE_EEHALFCLK(BGE_HALFCLK_384SCL));
	IODelay(20);
	
	//Issue read EEPROM command
	CSR_WRITE_4(sc, BGE_EE_ADDR, BGE_EE_READCMD | addr);
	
	// wait til done
	for (i = 0; i < BGE_TIMEOUT * 10; i++)
	{
		IODelay(10);
		if (CSR_READ_4(sc, BGE_EE_ADDR) & BGE_EEADDR_DONE)
			break;
	}
	
	if (i == BGE_TIMEOUT)
	{
		DLOG("EEPROM read timed out");
		return 1;
	}
	
	//Get result
	byte = CSR_READ_4(sc, BGE_EE_DATA);
	
	*dest = (byte >> ((addr % 4) * 8)) & 0xFF;
	
	return 0;
}

void OSXBGE::bge_writembx (struct bge_softc *sc, int off, int val)
{
	if (sc->bge_asicrev == BGE_ASICREV_BCM5906)
		off += BGE_LPMBX_IRQ0_HI - BGE_MBX_IRQ0_HI;
	
	CSR_WRITE_4(sc, off, val);
}


int OSXBGE::bge_chipinit(struct bge_softc *sc)
{
	uint32_t dma_rw_ctl;
	int i;
	
	/* Set endianness before we access any non-PCI registers. */
	fPCIDevice->configWrite32(BGE_PCI_MISC_CTL, BGE_INIT);
	
	/*
	 * Check the 'ROM failed' bit on the RX CPU to see if
	 * self-tests passed. Skip this check when there's no
	 * EEPROM fitted, since in that case it will always
	 * fail.
	 */
	if ((sc->bge_flags & BGE_FLAG_EADDR) && CSR_READ_4(sc, BGE_RXCPU_MODE) & BGE_RXCPUMODE_ROMFAIL) {
		DLOG("RX CPU self-diagnostics failed!\n");
		return (-1);
	}
	
	/* Clear the MAC control register */
	CSR_WRITE_4(sc, BGE_MAC_MODE, 0);
	
	/*
	 * Clear the MAC statistics block in the NIC's
	 * internal memory.
	 */
	for (i = BGE_STATS_BLOCK; i < BGE_STATS_BLOCK_END + 1; i += sizeof(uint32_t))
		BGE_MEMWIN_WRITE(i, 0);
	
	for (i = BGE_STATUS_BLOCK; i < BGE_STATUS_BLOCK_END + 1; i += sizeof(uint32_t))
		BGE_MEMWIN_WRITE(i, 0);
	
	/*
	 * Set up the PCI DMA control register.
	 */
	dma_rw_ctl = BGE_PCIDMARWCTL_RD_CMD_SHIFT(6) | BGE_PCIDMARWCTL_WR_CMD_SHIFT(7);
	
	if (sc->bge_flags & BGE_FLAG_PCIE)
	{
		/* Read watermark not used, 128 bytes for write. */
		dma_rw_ctl |= BGE_PCIDMARWCTL_WR_WAT_SHIFT(3);
	} 
	else if (sc->bge_flags & BGE_FLAG_PCIX) 
	{
		if (BGE_IS_5714_FAMILY(sc)) 
		{
			/* 256 bytes for read and write. */
			dma_rw_ctl |= BGE_PCIDMARWCTL_RD_WAT_SHIFT(2) | BGE_PCIDMARWCTL_WR_WAT_SHIFT(2);
			dma_rw_ctl |= (sc->bge_asicrev == BGE_ASICREV_BCM5780) ? BGE_PCIDMARWCTL_ONEDMA_ATONCE_GLOBAL : BGE_PCIDMARWCTL_ONEDMA_ATONCE_LOCAL;
		} 
		else if (sc->bge_asicrev == BGE_ASICREV_BCM5704) 
		{
			/* 1536 bytes for read, 384 bytes for write. */
			dma_rw_ctl |= BGE_PCIDMARWCTL_RD_WAT_SHIFT(7) | BGE_PCIDMARWCTL_WR_WAT_SHIFT(3);
		} 
		else 
		{
			/* 384 bytes for read and write. */
			dma_rw_ctl |= BGE_PCIDMARWCTL_RD_WAT_SHIFT(3) | BGE_PCIDMARWCTL_WR_WAT_SHIFT(3) | 0x0F;
		}
		
		if (sc->bge_asicrev == BGE_ASICREV_BCM5703 || sc->bge_asicrev == BGE_ASICREV_BCM5704) 
		{
			uint32_t tmp;
			
			/* Set ONE_DMA_AT_ONCE for hardware workaround. */
			tmp = CSR_READ_4(sc, BGE_PCI_CLKCTL) & 0x1F;
			if (tmp == 6 || tmp == 7)
				dma_rw_ctl |= BGE_PCIDMARWCTL_ONEDMA_ATONCE_GLOBAL;
			
			/* Set PCI-X DMA write workaround. */
			dma_rw_ctl |= BGE_PCIDMARWCTL_ASRT_ALL_BE;
		}
	} 
	else 
	{
		/* Conventional PCI bus: 256 bytes for read and write. */
		dma_rw_ctl |= BGE_PCIDMARWCTL_RD_WAT_SHIFT(7) | BGE_PCIDMARWCTL_WR_WAT_SHIFT(7);
		
		if (sc->bge_asicrev != BGE_ASICREV_BCM5705 && sc->bge_asicrev != BGE_ASICREV_BCM5750)
			dma_rw_ctl |= 0x0F;
	}
	
	if (sc->bge_asicrev == BGE_ASICREV_BCM5700 || sc->bge_asicrev == BGE_ASICREV_BCM5701)
		dma_rw_ctl |= BGE_PCIDMARWCTL_USE_MRM | BGE_PCIDMARWCTL_ASRT_ALL_BE;
	
	if (sc->bge_asicrev == BGE_ASICREV_BCM5703 || sc->bge_asicrev == BGE_ASICREV_BCM5704)
		dma_rw_ctl &= ~BGE_PCIDMARWCTL_MINDMA;
	
	fPCIDevice->configWrite32(BGE_PCI_DMA_RW_CTL, dma_rw_ctl);
	
	/*
	 * Set up general mode register.
	 */
	CSR_WRITE_4(sc, BGE_MODE_CTL, BGE_DMA_SWAP_OPTIONS |
				BGE_MODECTL_MAC_ATTN_INTR | BGE_MODECTL_HOST_SEND_BDS |
				BGE_MODECTL_TX_NO_PHDR_CSUM);
	
	/*
	 * Tell the firmware the driver is running
	 */
	if (sc->bge_asf_mode & ASF_STACKUP)
	{
		DLOG("Driver online");
		BGE_SETBIT(sc, BGE_MODE_CTL, BGE_MODECTL_STACKUP);
	}
	
	/*
	 * Disable memory write invalidate.  Apparently it is not supported
	 * properly by these devices.
	 */
	PCI_CLRBIT(BGE_PCI_CMD, PCIM_CMD_MWIEN);
	
	/* Set the timer prescaler (always 66Mhz) */
	CSR_WRITE_4(sc, BGE_MISC_CFG, BGE_32BITTIME_66MHZ);
	
	if (sc->bge_asicrev == BGE_ASICREV_BCM5906)
	{
		IODelay(40); /* XXX */

		/*Put PHY into ready state*/
		BGE_CLRBIT(sc, BGE_MISC_CFG, BGE_MISCCFG_EPHY_IDDQ);
		CSR_READ_4(sc, BGE_MISC_CFG); /* Flush */
		IODelay(40);
	}
	
	return (0);
}

int OSXBGE::bge_reset()
{
	uint32_t cachesize, command, pcistate, reset;
	int i, val = 0;
	
	/* Save some important PCI state. */
	cachesize = fPCIDevice->configRead32(BGE_PCI_CACHESZ);
	command = fPCIDevice->configRead32(BGE_PCI_CMD);
	pcistate = fPCIDevice->configRead32(BGE_PCI_PCISTATE);
	
	fPCIDevice->configWrite32(BGE_PCI_MISC_CTL,
							  BGE_PCIMISCCTL_INDIRECT_ACCESS | BGE_PCIMISCCTL_MASK_PCI_INTR |
							  BGE_HIF_SWAP_OPTIONS | BGE_PCIMISCCTL_PCISTATE_RW);
	
	/* Disable fastboot on controllers that support it. */
	if (sc->bge_asicrev == BGE_ASICREV_BCM5752 ||
	    sc->bge_asicrev == BGE_ASICREV_BCM5755 ||
	    sc->bge_asicrev == BGE_ASICREV_BCM5787) {
		//		if (bootverbose)
		DLOG("Disabling fastboot\n");
		CSR_WRITE_4(sc, BGE_FASTBOOT_PC, 0x0);
	}
	
	/*
	 * Write the magic number to SRAM at offset 0xB50.
	 * When firmware finishes its initialization it will
	 * write ~BGE_MAGIC_NUMBER to the same location.
	 */
	bge_writemem_ind(sc, BGE_SOFTWARE_GENCOMM, BGE_MAGIC_NUMBER);
	
	reset = BGE_MISCCFG_RESET_CORE_CLOCKS | BGE_32BITTIME_66MHZ;
	
	/* XXX: Broadcom Linux driver. */
	if (sc->bge_flags & BGE_FLAG_PCIE) {
		if (CSR_READ_4(sc, 0x7E2C) == 0x60)	/* PCIE 1.0 */
		{
			CSR_WRITE_4(sc, 0x7E2C, 0x20);
		}
		if (sc->bge_chipid != BGE_CHIPID_BCM5750_A0) 
		{
			/* Prevent PCIE link training during global reset */
			CSR_WRITE_4(sc, BGE_MISC_CFG, 1 << 29);
			reset |= 1 << 29;
		}
	}
	
	/* 
	 * Set GPHY Power Down Override to leave GPHY
	 * powered up in D0 uninitialized.
	 */
	if (BGE_IS_5705_PLUS(sc))
	{
		reset |= 0x04000000;
	}
	
	/* Issue global reset */
	if (BGE_IS_575X_PLUS(sc) && !BGE_IS_5714_FAMILY(sc) && (sc->bge_asicrev != BGE_ASICREV_BCM5906))
	{
		if (sc->bge_flags & BGE_FLAG_PCIE)
		{
			bge_writemem_direct(sc, BGE_MISC_CFG, reset);
		}
		else
		{
			bge_writemem_ind(sc, BGE_MISC_CFG, reset);
		}
	}
	else
	{
		bge_writereg_ind(sc, BGE_MISC_CFG, reset);
	}
	
	if (sc->bge_asicrev == BGE_ASICREV_BCM5906)
	{
		val = CSR_READ_4(sc, BGE_VCPU_STATUS);
		CSR_WRITE_4(sc, BGE_VCPU_STATUS, val | BGE_VCPU_STATUS_DRV_RESET);
		val = CSR_READ_4(sc, BGE_VCPU_EXT_CTRL);
		CSR_WRITE_4(sc, BGE_VCPU_EXT_CTRL, val & ~BGE_VCPU_EXT_CTRL_HALT_CPU);
	}	
	
	IODelay(1000);
	
	/* XXX: Broadcom Linux driver. */
	if (sc->bge_flags & BGE_FLAG_PCIE) 
	{
		if (sc->bge_chipid == BGE_CHIPID_BCM5750_A0) 
		{
			uint32_t v;
			
			IODelay(500000); /* wait for link training to complete */
			v = fPCIDevice->configRead32(0xC4);
			fPCIDevice->configWrite32(0xC4, v | (1 << 15));
		}
		/*
		 * Set PCIE max payload size to 128 bytes and clear error
		 * status.
		 */
		fPCIDevice->configWrite32(0xD8, 0xF5000);
	}
	
	/* Reset some of the PCI state that got zapped by reset. */
	fPCIDevice->configWrite32(BGE_PCI_MISC_CTL,
							  BGE_PCIMISCCTL_INDIRECT_ACCESS | BGE_PCIMISCCTL_MASK_PCI_INTR |
							  BGE_HIF_SWAP_OPTIONS | BGE_PCIMISCCTL_PCISTATE_RW);
	fPCIDevice->configWrite32(BGE_PCI_CACHESZ, cachesize);
	fPCIDevice->configWrite32(BGE_PCI_CMD, command);
	
	if (BGE_IS_575X_PLUS(sc) && !BGE_IS_5714_FAMILY(sc) && (sc->bge_asicrev != BGE_ASICREV_BCM5906))
	{
		if (sc->bge_flags & BGE_FLAG_PCIE)
		{
			bge_writemem_direct(sc, BGE_MISC_CFG, BGE_32BITTIME_66MHZ);
		}
		else
		{
			bge_writemem_ind(sc, BGE_MISC_CFG, BGE_32BITTIME_66MHZ);
		}
	}
	else
	{
		bge_writereg_ind(sc, BGE_MISC_CFG, reset);
	}	
	/* Re-enable MSI, if neccesary, and enable the memory arbiter. */
	if (BGE_IS_5714_FAMILY(sc)) {
		uint32_t val;
		
		/* This chip disables MSI on reset. */
		if (sc->bge_flags & BGE_FLAG_MSI) 
		{
			val = fPCIDevice->configRead16(BGE_PCI_MSI_CTL);
			fPCIDevice->configWrite16(BGE_PCI_MSI_CTL, val | PCIM_MSICTRL_MSI_ENABLE);
			val = CSR_READ_4(sc, BGE_MSI_MODE);
			CSR_WRITE_4(sc, BGE_MSI_MODE, val | BGE_MSIMODE_ENABLE);
		}
		val = CSR_READ_4(sc, BGE_MARB_MODE);
		CSR_WRITE_4(sc, BGE_MARB_MODE, BGE_MARBMODE_ENABLE | val);
	}
	else
	{
		CSR_WRITE_4(sc, BGE_MARB_MODE, BGE_MARBMODE_ENABLE);
	}
	
	//Poll firmware
	
	if (sc->bge_asicrev == BGE_ASICREV_BCM5906)
	{
		for (i = 0; i < BGE_TIMEOUT; i++)
		{
			val = CSR_READ_4(sc, BGE_VCPU_STATUS);
			
			if (val & BGE_VCPU_STATUS_INIT_DONE)
			{
				break;
			}
			
			IODelay(100);
		}
		if (i == BGE_TIMEOUT)
		{
			DLOG("Reset timed out");
			return 1;
		}
	}
	else
	{
		/*
		 * Poll until we see the 1's complement of the magic number.
		 * This indicates that the firmware initialization is complete.
		 * We expect this to fail if no EEPROM is fitted though.
		 */
		for (i = 0; i < BGE_TIMEOUT; i++) {
			IODelay(10);
			val = bge_readmem_ind(sc, BGE_SOFTWARE_GENCOMM);
			if (val == ~BGE_MAGIC_NUMBER)
			{
				DLOG("Found ~BGE_MAGIC_NUMBER");
				break;
			}
		}
		
		if ((sc->bge_flags & BGE_FLAG_EADDR) && i == BGE_TIMEOUT)
		{
			DLOG("firmware handshake timed out, found 0x%08x", val);
		}
	}	
	
	/*
	 * XXX Wait for the value of the PCISTATE register to
	 * return to its original pre-reset state. This is a
	 * fairly good indicator of reset completion. If we don't
	 * wait for the reset to fully complete, trying to read
	 * from the device's non-PCI registers may yield garbage
	 * results.
	 */
	for (i = 0; i < BGE_TIMEOUT; i++)
	{
		if (fPCIDevice->configRead32(BGE_PCI_PCISTATE) == pcistate)
		{
			break;
		}
		
		IODelay(10);
	}
	
	if (sc->bge_flags & BGE_FLAG_PCIE)
	{
		DLOG("PCI-E Board");
		reset = bge_readmem_ind(sc, 0x7C00);
		bge_writemem_ind(sc, 0x7C00, reset | (1 << 25));
	}
	
	/* Fix up byte swapping. */
	CSR_WRITE_4(sc, BGE_MODE_CTL, BGE_DMA_SWAP_OPTIONS | BGE_MODECTL_BYTESWAP_DATA);
	
	/* Tell the ASF firmware we are up */
	if (sc->bge_asf_mode & ASF_STACKUP)
	{
		DLOG("Report Online to ASF");
		BGE_SETBIT(sc, BGE_MODE_CTL, BGE_MODECTL_STACKUP);
	}
	
	CSR_WRITE_4(sc, BGE_MAC_MODE, 0);
	
	/*
	 * The 5704 in TBI mode apparently needs some special
	 * adjustment to insure the SERDES drive level is set
	 * to 1.2V.
	 */
	if (sc->bge_asicrev == BGE_ASICREV_BCM5704 && sc->bge_flags & BGE_FLAG_TBI)
	{
		uint32_t serdescfg;
		
		serdescfg = CSR_READ_4(sc, BGE_SERDES_CFG);
		serdescfg = (serdescfg & ~0xFFF) | 0x880;
		CSR_WRITE_4(sc, BGE_SERDES_CFG, serdescfg);
	}
	
	/* XXX: Broadcom Linux driver. */
	if (sc->bge_flags & BGE_FLAG_PCIE && sc->bge_chipid != BGE_CHIPID_BCM5750_A0)
	{
		uint32_t v;
		
		v = CSR_READ_4(sc, 0x7C00);
		CSR_WRITE_4(sc, 0x7C00, v | (1 << 25));
	}
	IODelay(10000);
	
	return 0;
}

uint32_t OSXBGE::CFSwapInt32(uint32_t arg) {
    uint32_t result;
    result = ((arg & 0xFF) << 24) | ((arg & 0xFF00) << 8) | ((arg >> 8) & 0xFF00) | ((arg >> 24) & 0xFF);
    return result;
}

int OSXBGE::bge_wait_bit(struct bge_softc *sc, u_int32_t reg, u_int32_t bit, u_long timeout, const int clear)
{
	u_long i;
	int use_sleep = 0;
	
	if (timeout >= 10000) {
		use_sleep = 1;
		timeout /= 1000;
	}
	
	for (i = 0; i < timeout; i++)
	{
		u_int32_t val = CSR_READ_4(sc, reg);
		
		if (clear && !(val & bit))
			break;
		if(!clear && (val & bit))
			break;
		
		if (use_sleep)
			IOSleep(10);
		else
			IODelay(10);
	}
	
	//if (i == timeout) {
	//		DLOG("Timeout waiting for PHY register!");
	//		return -1;
	//}
	
	return 0;
}

void OSXBGE::bge_stop_fw(struct bge_softc * sc)
{
	int i;
	
	if (sc->bge_asf_mode)
	{
		bge_writemem_ind(sc, BGE_SOFTWARE_GENCOMM_FW, BGE_FW_PAUSE);
		CSR_WRITE_4(sc, BGE_CPU_EVENT, CSR_READ_4(sc, BGE_CPU_EVENT) | (1 << 14));
		
		for(i = 0; i < 100; i++)
		{
			if (!(CSR_READ_4(sc, BGE_CPU_EVENT) & (1 << 14)))
				break;
			
			IODelay(10);
		}
	}
}

static int bge_allow_asf = 1;
void OSXBGE::bge_setup(struct bge_softc *sc)
{
	uint32_t reg;
	
	// Initialize bge_softc
	sc->bge_dev = this;
	sc->bge_chipid = fPCIDevice->configRead32(BGE_PCI_MISC_CTL) & BGE_PCIMISCCTL_ASICREV;
	sc->bge_asicrev = BGE_ASICREV(sc->bge_chipid);
	sc->bge_chiprev = BGE_CHIPREV(sc->bge_chipid);
	sc->bge_flags |= BGE_FLAG_EADDR;
	
	//Disable Ethernet@Wirespeed for 5700,5906, and 5705 A0 and A1 chips
	if (sc->bge_asicrev != BGE_ASICREV_BCM5700 &&
		sc->bge_asicrev != BGE_ASICREV_BCM5906 &&
		sc->bge_chipid != BGE_CHIPID_BCM5705_A0 &&
		sc->bge_chipid != BGE_CHIPID_BCM5705_A1)
	{
		sc->bge_flags |= BGE_FLAG_WIRESPEED;
	}
	
	/* Save chipset family. */
	switch (sc->bge_asicrev) {
		case BGE_ASICREV_BCM5700:
		case BGE_ASICREV_BCM5701:
		case BGE_ASICREV_BCM5703:
		case BGE_ASICREV_BCM5704:
			sc->bge_flags |= BGE_FLAG_5700_FAMILY | BGE_FLAG_JUMBO;
			DLOG("Found a 5700 chipset");
			break;
		case BGE_ASICREV_BCM5714_A0:
		case BGE_ASICREV_BCM5780:
		case BGE_ASICREV_BCM5714:
			sc->bge_flags |= BGE_FLAG_5714_FAMILY /* | BGE_FLAG_JUMBO */;
			/* FALLTHRU */
		case BGE_ASICREV_BCM5750:
		case BGE_ASICREV_BCM5752:
		case BGE_ASICREV_BCM5755:
		case BGE_ASICREV_BCM5787:  //also applies to BGE_ASICREV_BCM5754
		case BGE_ASICREV_BCM5906:
			sc->bge_flags |= BGE_FLAG_575X_PLUS;
			/* FALLTHRU */
		case BGE_ASICREV_BCM5705:
			sc->bge_flags |= BGE_FLAG_5705_PLUS;
			DLOG("Found a 5906");
			break;
	}
	
	// Set various bug flags
	if (sc->bge_chipid == BGE_CHIPID_BCM5701_A0 || sc->bge_chipid == BGE_CHIPID_BCM5701_B0)
	{
		sc->bge_flags |= BGE_FLAG_CRC_BUG;
	}
	
	if (sc->bge_chiprev == BGE_CHIPREV_5703_AX || sc->bge_chiprev == BGE_CHIPREV_5704_AX)
	{
		sc->bge_flags |= BGE_FLAG_ADC_BUG;
	}
	
	if (sc->bge_chipid == BGE_CHIPID_BCM5704_A0)
	{
		sc->bge_flags |= BGE_FLAG_5704_A0_BUG;
	}
	
	if (BGE_IS_5705_PLUS(sc) && !(sc->bge_flags & BGE_FLAG_ADJUST_TRIM))
	{
		if (sc->bge_asicrev == BGE_ASICREV_BCM5755 || sc->bge_asicrev == BGE_ASICREV_BCM5787)
		{
			if (sc->bge_flags != BGE_CHIPID_BCM5722_A0)
			{
				sc->bge_flags |= BGE_FLAG_JITTER_BUG;
			}
		}
		else if (sc->bge_asicrev != BGE_ASICREV_BCM5906)
		{
			sc->bge_flags |= BGE_FLAG_BER_BUG;
		}
	}
	
	/*
	 * Check if this is a PCI-X or PCI Express device.
	 */
	if (BGE_IS_5705_PLUS(sc)) 
	{
		reg = fPCIDevice->configRead32(BGE_PCIE_CAPID_REG);
		if ((reg & 0xFF) == BGE_PCIE_CAPID)
		{
			sc->bge_flags |= BGE_FLAG_PCIE;
		}
	} 
	else 
	{
		/*
		 * Check if the device is in PCI-X Mode.
		 * (This bit is not valid on PCI Express controllers.)
		 */
		if ((fPCIDevice->configRead32(BGE_PCI_PCISTATE) & BGE_PCISTATE_PCI_BUSMODE) == 0)
		{
			sc->bge_flags |= BGE_FLAG_PCIX;
		}
	}
	
	if (bge_reset())
	{
		DLOG("Reset failed!");
	}
	
	sc->bge_asf_mode = 0;
	if (bge_allow_asf && (bge_readmem_ind(sc, BGE_SOFTWARE_GENCOMM_SIG) == BGE_MAGIC_NUMBER))
	{
		if (bge_readmem_ind(sc, BGE_SOFTWARE_GENCOMM_NICCFG) & BGE_HWCFG_ASF) 
		{
			sc->bge_asf_mode |= ASF_ENABLE;
			sc->bge_asf_mode |= ASF_STACKUP;
			if (sc->bge_asicrev == BGE_ASICREV_BCM5750)
			{
				sc->bge_asf_mode |= ASF_NEW_HANDSHAKE;
			}
		}
	}
	
	/* Turn on transmitter. */
	BGE_SETBIT(sc, BGE_TX_MODE, BGE_TXMODE_ENABLE);
	
	/* Turn on receiver. */
	BGE_SETBIT(sc, BGE_RX_MODE, BGE_RXMODE_ENABLE);
	
	/* Tell firmware we're alive. */
	BGE_SETBIT(sc, BGE_MODE_CTL, BGE_MODECTL_STACKUP);
	
	//Enable Interrupts
	BGE_SETBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_CLEAR_INTA);
	BGE_CLRBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_MASK_PCI_INTR);
	bge_writembx(sc, BGE_MBX_IRQ0_LO, 0);
}

int OSXBGE::bge_blockinit(struct bge_softc * sc)
{
	struct bge_rcb *rcb;
	bus_size_t vrcb;
	bge_hostaddr taddr;
	uint32_t val;
	int i;
	
	/*
	 * Initialize the memory window pointer register so that
	 * we can access the first 32K of internal NIC RAM. This will
	 * allow us to set up the TX send ring RCBs and the RX return
	 * ring RCBs, plus other things which live in NIC memory.
	 */
	CSR_WRITE_4(sc, BGE_PCI_MEMWIN_BASEADDR, 0);
	
	/* Note: the BCM5704 has a smaller mbuf space than other chips. */
	
	if (!(BGE_IS_5705_PLUS(sc))) {
		/* Configure mbuf memory pool */
		CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_BASEADDR, BGE_BUFFPOOL_1);
		if (sc->bge_asicrev == BGE_ASICREV_BCM5704)
			CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_LEN, 0x10000);
		else
			CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_LEN, 0x18000);
		
		/* Configure DMA resource pool */
		CSR_WRITE_4(sc, BGE_BMAN_DMA_DESCPOOL_BASEADDR,
					BGE_DMA_DESCRIPTORS);
		CSR_WRITE_4(sc, BGE_BMAN_DMA_DESCPOOL_LEN, 0x2000);
	}
	
	/* Configure mbuf pool watermarks */
	if (!BGE_IS_5705_PLUS(sc)) {
		CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_READDMA_LOWAT, 0x50);
		CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_MACRX_LOWAT, 0x20);
		CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_HIWAT, 0x60);
	} else if (sc->bge_asicrev == BGE_ASICREV_BCM5906) {
		CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_READDMA_LOWAT, 0x0);
		CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_MACRX_LOWAT, 0x04);
		CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_HIWAT, 0x10);
	} else {
		CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_READDMA_LOWAT, 0x0);
		CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_MACRX_LOWAT, 0x10);
		CSR_WRITE_4(sc, BGE_BMAN_MBUFPOOL_HIWAT, 0x60);
	}
	
	/* Configure DMA resource watermarks */
	CSR_WRITE_4(sc, BGE_BMAN_DMA_DESCPOOL_LOWAT, 5);
	CSR_WRITE_4(sc, BGE_BMAN_DMA_DESCPOOL_HIWAT, 10);
	
	/* Enable buffer manager */
	if (!(BGE_IS_5705_PLUS(sc))) 
	{
		CSR_WRITE_4(sc, BGE_BMAN_MODE,
					BGE_BMANMODE_ENABLE | BGE_BMANMODE_LOMBUF_ATTN);
		
		/* Poll for buffer manager start indication */
		for (i = 0; i < BGE_TIMEOUT; i++) 
		{
			IODelay(10);
			if (CSR_READ_4(sc, BGE_BMAN_MODE) & BGE_BMANMODE_ENABLE)
				break;
		}
		
		if (i == BGE_TIMEOUT) 
		{
			DLOG("OSXBGE::bge_blockinit - Buffer manager failed to start");
			return -1;
		}
	}
	
	/* Enable flow-through queues */
	CSR_WRITE_4(sc, BGE_FTQ_RESET, 0xFFFFFFFF);
	CSR_WRITE_4(sc, BGE_FTQ_RESET, 0);
	
	/* Wait until queue initialization is complete */
	for (i = 0; i < BGE_TIMEOUT; i++) {
		IODelay(10);
		if (CSR_READ_4(sc, BGE_FTQ_RESET) == 0)
			break;
	}
	
	if (i == BGE_TIMEOUT) {
		DLOG("OSXBGE::bge_blockinit - flow-through queue init failed\n");
		return -1;
	}
	
	/* Initialize the standard RX ring control block */
	rcb = &sc->bge_ldata.bge_info.bge_std_rx_rcb;
	rcb->bge_hostaddr.bge_addr_lo = BGE_ADDR_LO(sc->bge_ldata.bge_rx_std_ring_paddr);
	rcb->bge_hostaddr.bge_addr_hi = BGE_ADDR_HI(sc->bge_ldata.bge_rx_std_ring_paddr);
	bus_dmamap_sync(sc->bge_cdata.bge_rx_std_ring_tag, sc->bge_cdata.bge_rx_std_ring_map, BUS_DMASYNC_PREREAD);
	if (BGE_IS_5705_PLUS(sc))
		rcb->bge_maxlen_flags = BGE_RCB_MAXLEN_FLAGS(512, 0);
	else
		rcb->bge_maxlen_flags = BGE_RCB_MAXLEN_FLAGS(BGE_MAX_FRAMELEN, 0);
	
	rcb->bge_nicaddr = BGE_STD_RX_RINGS;
	CSR_WRITE_4(sc, BGE_RX_STD_RCB_HADDR_HI, rcb->bge_hostaddr.bge_addr_hi);
	CSR_WRITE_4(sc, BGE_RX_STD_RCB_HADDR_LO, rcb->bge_hostaddr.bge_addr_lo);
	
	CSR_WRITE_4(sc, BGE_RX_STD_RCB_MAXLEN_FLAGS, rcb->bge_maxlen_flags);
	CSR_WRITE_4(sc, BGE_RX_STD_RCB_NICADDR, rcb->bge_nicaddr);
	
	/*
	 * Initialize the jumbo RX ring control block
	 * We set the 'ring disabled' bit in the flags
	 * field until we're actually ready to start
	 * using this ring (i.e. once we set the MTU
	 * high enough to require it).
	 */
	if (BGE_IS_JUMBO_CAPABLE(sc)) {
		rcb = &sc->bge_ldata.bge_info.bge_jumbo_rx_rcb;
		
		rcb->bge_hostaddr.bge_addr_lo =
		BGE_ADDR_LO(sc->bge_ldata.bge_rx_jumbo_ring_paddr);
		rcb->bge_hostaddr.bge_addr_hi =
		BGE_ADDR_HI(sc->bge_ldata.bge_rx_jumbo_ring_paddr);
		bus_dmamap_sync(sc->bge_cdata.bge_rx_jumbo_ring_tag,
						sc->bge_cdata.bge_rx_jumbo_ring_map,
						BUS_DMASYNC_PREREAD);
		rcb->bge_maxlen_flags = BGE_RCB_MAXLEN_FLAGS(0,
													 BGE_RCB_FLAG_USE_EXT_RX_BD | BGE_RCB_FLAG_RING_DISABLED);
		rcb->bge_nicaddr = BGE_JUMBO_RX_RINGS;
		CSR_WRITE_4(sc, BGE_RX_JUMBO_RCB_HADDR_HI,
					rcb->bge_hostaddr.bge_addr_hi);
		CSR_WRITE_4(sc, BGE_RX_JUMBO_RCB_HADDR_LO,
					rcb->bge_hostaddr.bge_addr_lo);
		
		CSR_WRITE_4(sc, BGE_RX_JUMBO_RCB_MAXLEN_FLAGS,
					rcb->bge_maxlen_flags);
		CSR_WRITE_4(sc, BGE_RX_JUMBO_RCB_NICADDR, rcb->bge_nicaddr);
		
		/* Set up dummy disabled mini ring RCB */
		rcb = &sc->bge_ldata.bge_info.bge_mini_rx_rcb;
		rcb->bge_maxlen_flags =
		BGE_RCB_MAXLEN_FLAGS(0, BGE_RCB_FLAG_RING_DISABLED);
		CSR_WRITE_4(sc, BGE_RX_MINI_RCB_MAXLEN_FLAGS,
					rcb->bge_maxlen_flags);
	}
	
	/*
	 * Set the BD ring replentish thresholds. The recommended
	 * values are 1/8th the number of descriptors allocated to
	 * each ring.
	 * XXX The 5754 requires a lower threshold, so it might be a
	 * requirement of all 575x family chips.  The Linux driver sets
	 * the lower threshold for all 5705 family chips as well, but there
	 * are reports that it might not need to be so strict.
	 *
	 * XXX Linux does some extra fiddling here for the 5906 parts as
	 * well.
	 */
	if (BGE_IS_5705_PLUS(sc))
		val = 8;
	else
		val = BGE_STD_RX_RING_CNT / 8;
	CSR_WRITE_4(sc, BGE_RBDI_STD_REPL_THRESH, val);
	CSR_WRITE_4(sc, BGE_RBDI_JUMBO_REPL_THRESH, BGE_JUMBO_RX_RING_CNT/8);
	
	/*
	 * Disable all unused send rings by setting the 'ring disabled'
	 * bit in the flags field of all the TX send ring control blocks.
	 * These are located in NIC memory.
	 */
	vrcb = BGE_MEMWIN_START + BGE_SEND_RING_RCB;
	for (i = 0; i < BGE_TX_RINGS_EXTSSRAM_MAX; i++) {
		RCB_WRITE_4(sc, vrcb, bge_maxlen_flags, BGE_RCB_MAXLEN_FLAGS(0, BGE_RCB_FLAG_RING_DISABLED));
		RCB_WRITE_4(sc, vrcb, bge_nicaddr, 0);
		vrcb += sizeof(struct bge_rcb);
	}
	
	/* Configure TX RCB 0 (we use only the first ring) */
	vrcb = BGE_MEMWIN_START + BGE_SEND_RING_RCB;
	BGE_HOSTADDR(taddr, sc->bge_ldata.bge_tx_ring_paddr);
	RCB_WRITE_4(sc, vrcb, bge_hostaddr.bge_addr_hi, taddr.bge_addr_hi);
	RCB_WRITE_4(sc, vrcb, bge_hostaddr.bge_addr_lo, taddr.bge_addr_lo);
	RCB_WRITE_4(sc, vrcb, bge_nicaddr,
				BGE_NIC_TXRING_ADDR(0, BGE_TX_RING_CNT));
	if (!(BGE_IS_5705_PLUS(sc)))
		RCB_WRITE_4(sc, vrcb, bge_maxlen_flags,
					BGE_RCB_MAXLEN_FLAGS(BGE_TX_RING_CNT, 0));
	
	/* Disable all unused RX return rings */
	vrcb = BGE_MEMWIN_START + BGE_RX_RETURN_RING_RCB;
	for (i = 0; i < BGE_RX_RINGS_MAX; i++) {
		RCB_WRITE_4(sc, vrcb, bge_hostaddr.bge_addr_hi, 0);
		RCB_WRITE_4(sc, vrcb, bge_hostaddr.bge_addr_lo, 0);
		RCB_WRITE_4(sc, vrcb, bge_maxlen_flags,
					BGE_RCB_MAXLEN_FLAGS(sc->bge_return_ring_cnt,
										 BGE_RCB_FLAG_RING_DISABLED));
		RCB_WRITE_4(sc, vrcb, bge_nicaddr, 0);
		bge_writembx(sc, BGE_MBX_RX_CONS0_LO +
					 (i * (sizeof(uint64_t))), 0);
		vrcb += sizeof(struct bge_rcb);
	}
	
	/* Initialize RX ring indexes */
	bge_writembx(sc, BGE_MBX_RX_STD_PROD_LO, 0);
	bge_writembx(sc, BGE_MBX_RX_JUMBO_PROD_LO, 0);
	bge_writembx(sc, BGE_MBX_RX_MINI_PROD_LO, 0);
	
	/*
	 * Set up RX return ring 0
	 * Note that the NIC address for RX return rings is 0x00000000.
	 * The return rings live entirely within the host, so the
	 * nicaddr field in the RCB isn't used.
	 */
	vrcb = BGE_MEMWIN_START + BGE_RX_RETURN_RING_RCB;
	BGE_HOSTADDR(taddr, sc->bge_ldata.bge_rx_return_ring_paddr);
	RCB_WRITE_4(sc, vrcb, bge_hostaddr.bge_addr_hi, taddr.bge_addr_hi);
	RCB_WRITE_4(sc, vrcb, bge_hostaddr.bge_addr_lo, taddr.bge_addr_lo);
	RCB_WRITE_4(sc, vrcb, bge_nicaddr, 0x00000000);
	RCB_WRITE_4(sc, vrcb, bge_maxlen_flags,
				BGE_RCB_MAXLEN_FLAGS(sc->bge_return_ring_cnt, 0));	
	
	/* Set random backoff seed for TX */
	CSR_WRITE_4(sc, BGE_TX_RANDOM_BACKOFF,
				IF_LLADDR(sc->bge_ifp)[0] + IF_LLADDR(sc->bge_ifp)[1] +
				IF_LLADDR(sc->bge_ifp)[2] + IF_LLADDR(sc->bge_ifp)[3] +
				IF_LLADDR(sc->bge_ifp)[4] + IF_LLADDR(sc->bge_ifp)[5] +
				BGE_TX_BACKOFF_SEED_MASK);
	
	/* Set inter-packet gap */
	CSR_WRITE_4(sc, BGE_TX_LENGTHS, 0x2620);
	
	/*
	 * Specify which ring to use for packets that don't match
	 * any RX rules.
	 */
	CSR_WRITE_4(sc, BGE_RX_RULES_CFG, 0x08);
	
	/*
	 * Configure number of RX lists. One interrupt distribution
	 * list, sixteen active lists, one bad frames class.
	 */
	CSR_WRITE_4(sc, BGE_RXLP_CFG, 0x181);
	
	/* Inialize RX list placement stats mask. */
	CSR_WRITE_4(sc, BGE_RXLP_STATS_ENABLE_MASK, 0x007FFFFF);
	CSR_WRITE_4(sc, BGE_RXLP_STATS_CTL, 0x1);
	
	/* Disable host coalescing until we get it set up */
	CSR_WRITE_4(sc, BGE_HCC_MODE, 0x00000000);
	
	/* Poll to make sure it's shut down. */
	for (i = 0; i < BGE_TIMEOUT; i++) {
		IODelay(10);
		if (!(CSR_READ_4(sc, BGE_HCC_MODE) & BGE_HCCMODE_ENABLE))
			break;
	}
	
	if (i == BGE_TIMEOUT) {
		DLOG("OSXBGE::bge_blockinit - host coalescing engine failed to idle\n");
		return -1;
	}
	
	/* Set up host coalescing defaults */
	CSR_WRITE_4(sc, BGE_HCC_RX_COAL_TICKS, sc->bge_rx_coal_ticks);
	CSR_WRITE_4(sc, BGE_HCC_TX_COAL_TICKS, sc->bge_tx_coal_ticks);
	CSR_WRITE_4(sc, BGE_HCC_RX_MAX_COAL_BDS, sc->bge_rx_max_coal_bds);
	CSR_WRITE_4(sc, BGE_HCC_TX_MAX_COAL_BDS, sc->bge_tx_max_coal_bds);
	if (!(BGE_IS_5705_PLUS(sc))) {
		CSR_WRITE_4(sc, BGE_HCC_RX_COAL_TICKS_INT, 0);
		CSR_WRITE_4(sc, BGE_HCC_TX_COAL_TICKS_INT, 0);
	}
	CSR_WRITE_4(sc, BGE_HCC_RX_MAX_COAL_BDS_INT, 1);
	CSR_WRITE_4(sc, BGE_HCC_TX_MAX_COAL_BDS_INT, 1);
	
	/* Set up address of statistics block */
	if (!(BGE_IS_5705_PLUS(sc))) {
		CSR_WRITE_4(sc, BGE_HCC_STATS_ADDR_HI,
					BGE_ADDR_HI(sc->bge_ldata.bge_stats_paddr));
		CSR_WRITE_4(sc, BGE_HCC_STATS_ADDR_LO,
					BGE_ADDR_LO(sc->bge_ldata.bge_stats_paddr));
		CSR_WRITE_4(sc, BGE_HCC_STATS_BASEADDR, BGE_STATS_BLOCK);
		CSR_WRITE_4(sc, BGE_HCC_STATUSBLK_BASEADDR, BGE_STATUS_BLOCK);
		CSR_WRITE_4(sc, BGE_HCC_STATS_TICKS, sc->bge_stat_ticks);
	}
	
	/* Set up address of status block */
	CSR_WRITE_4(sc, BGE_HCC_STATUSBLK_ADDR_HI,
				BGE_ADDR_HI(sc->bge_ldata.bge_status_block_paddr));
	CSR_WRITE_4(sc, BGE_HCC_STATUSBLK_ADDR_LO,
				BGE_ADDR_LO(sc->bge_ldata.bge_status_block_paddr));
	sc->bge_ldata.bge_status_block->bge_idx[0].bge_rx_prod_idx = 0;
	sc->bge_ldata.bge_status_block->bge_idx[0].bge_tx_cons_idx = 0;
	
	/* Turn on host coalescing state machine */
	CSR_WRITE_4(sc, BGE_HCC_MODE, BGE_HCCMODE_ENABLE);
	
	/* Turn on RX BD completion state machine and enable attentions */
	CSR_WRITE_4(sc, BGE_RBDC_MODE,
				BGE_RBDCMODE_ENABLE | BGE_RBDCMODE_ATTN);
	
	/* Turn on RX list placement state machine */
	CSR_WRITE_4(sc, BGE_RXLP_MODE, BGE_RXLPMODE_ENABLE);
	
	/* Turn on RX list selector state machine. */
	if (!(BGE_IS_5705_PLUS(sc)))
		CSR_WRITE_4(sc, BGE_RXLS_MODE, BGE_RXLSMODE_ENABLE);
	
	/* Turn on DMA, clear stats */
	CSR_WRITE_4(sc, BGE_MAC_MODE, BGE_MACMODE_TXDMA_ENB |
				BGE_MACMODE_RXDMA_ENB | BGE_MACMODE_RX_STATS_CLEAR |
				BGE_MACMODE_TX_STATS_CLEAR | BGE_MACMODE_RX_STATS_ENB |
				BGE_MACMODE_TX_STATS_ENB | BGE_MACMODE_FRMHDR_DMA_ENB |
				((sc->bge_flags & BGE_FLAG_TBI) ?
				 BGE_PORTMODE_TBI : BGE_PORTMODE_MII));
	
	/* Set misc. local control, enable interrupts on attentions */
	CSR_WRITE_4(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_INTR_ONATTN);
	
#ifdef notdef
	/* Assert GPIO pins for PHY reset */
	BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_MISCIO_OUT0 |
			   BGE_MLC_MISCIO_OUT1 | BGE_MLC_MISCIO_OUT2);
	BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_MISCIO_OUTEN0 |
			   BGE_MLC_MISCIO_OUTEN1 | BGE_MLC_MISCIO_OUTEN2);
#endif
	
	/* Turn on DMA completion state machine */
	if (!(BGE_IS_5705_PLUS(sc)))
		CSR_WRITE_4(sc, BGE_DMAC_MODE, BGE_DMACMODE_ENABLE);
	
	val = BGE_WDMAMODE_ENABLE | BGE_WDMAMODE_ALL_ATTNS;
	
	/* Enable host coalescing bug fix. */
	if (sc->bge_asicrev == BGE_ASICREV_BCM5755 ||
	    sc->bge_asicrev == BGE_ASICREV_BCM5787)
		val |= 1 << 29;
	
	/* Turn on write DMA state machine */
	CSR_WRITE_4(sc, BGE_WDMA_MODE, val);
	IODelay(40);
	
	/* Turn on read DMA state machine */
	val = BGE_RDMAMODE_ENABLE | BGE_RDMAMODE_ALL_ATTNS;
	if (sc->bge_flags & BGE_FLAG_PCIE)
		val |= BGE_RDMAMODE_FIFO_LONG_BURST;
	CSR_WRITE_4(sc, BGE_RDMA_MODE, val);
	IODelay(40);
	
	/* Turn on RX data completion state machine */
	CSR_WRITE_4(sc, BGE_RDC_MODE, BGE_RDCMODE_ENABLE);
	
	/* Turn on RX BD initiator state machine */
	CSR_WRITE_4(sc, BGE_RBDI_MODE, BGE_RBDIMODE_ENABLE);
	
	/* Turn on RX data and RX BD initiator state machine */
	CSR_WRITE_4(sc, BGE_RDBDI_MODE, BGE_RDBDIMODE_ENABLE);
	
	/* Turn on Mbuf cluster free state machine */
	if (!(BGE_IS_5705_PLUS(sc)))
		CSR_WRITE_4(sc, BGE_MBCF_MODE, BGE_MBCFMODE_ENABLE);
	
	/* Turn on send BD completion state machine */
	CSR_WRITE_4(sc, BGE_SBDC_MODE, BGE_SBDCMODE_ENABLE);
	
	/* Turn on send data completion state machine */
	CSR_WRITE_4(sc, BGE_SDC_MODE, BGE_SDCMODE_ENABLE);
	
	/* Turn on send data initiator state machine */
	CSR_WRITE_4(sc, BGE_SDI_MODE, BGE_SDIMODE_ENABLE);
	
	/* Turn on send BD initiator state machine */
	CSR_WRITE_4(sc, BGE_SBDI_MODE, BGE_SBDIMODE_ENABLE);
	
	/* Turn on send BD selector state machine */
	CSR_WRITE_4(sc, BGE_SRS_MODE, BGE_SRSMODE_ENABLE);
	
	CSR_WRITE_4(sc, BGE_SDI_STATS_ENABLE_MASK, 0x007FFFFF);
	CSR_WRITE_4(sc, BGE_SDI_STATS_CTL,
				BGE_SDISTATSCTL_ENABLE | BGE_SDISTATSCTL_FASTER);
	
	/* ack/clear link change events */
	CSR_WRITE_4(sc, BGE_MAC_STS, BGE_MACSTAT_SYNC_CHANGED |
				BGE_MACSTAT_CFG_CHANGED | BGE_MACSTAT_MI_COMPLETE |
				BGE_MACSTAT_LINK_CHANGED);
	CSR_WRITE_4(sc, BGE_MI_STS, 0);
	
	/* Enable PHY auto polling (for MII/GMII only) */
	if (sc->bge_flags & BGE_FLAG_TBI) {
		CSR_WRITE_4(sc, BGE_MI_STS, BGE_MISTS_LINK);
	} else {
		BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL | (10 << 16));
		if (sc->bge_asicrev == BGE_ASICREV_BCM5700 &&
		    sc->bge_chipid != BGE_CHIPID_BCM5700_B2)
			CSR_WRITE_4(sc, BGE_MAC_EVT_ENB,
						BGE_EVTENB_MI_INTERRUPT);
	}
	
	/*
	 * Clear any pending link state attention.
	 * Otherwise some link state change events may be lost until attention
	 * is cleared by bge_intr() -> bge_link_upd() sequence.
	 * It's not necessary on newer BCM chips - perhaps enabling link
	 * state change attentions implies clearing pending attention.
	 */
	CSR_WRITE_4(sc, BGE_MAC_STS, BGE_MACSTAT_SYNC_CHANGED |
				BGE_MACSTAT_CFG_CHANGED | BGE_MACSTAT_MI_COMPLETE |
				BGE_MACSTAT_LINK_CHANGED);
	
	/* Enable link state change attentions. */
	BGE_SETBIT(sc, BGE_MAC_EVT_ENB, BGE_EVTENB_LINK_CHANGED);
	
	return 0;	
}

void OSXBGE::bge_sig_pre_reset(struct bge_softc *sc, int type)
{
	if (sc->bge_asf_mode)
		bge_writemem_ind(sc, BGE_SOFTWARE_GENCOMM, BGE_MAGIC_NUMBER);
	
	if(sc->bge_asf_mode & ASF_NEW_HANDSHAKE)
	{
		switch (type)
		{
			case BGE_RESET_START:
				bge_writemem_ind(sc, BGE_SDI_STATUS, 0x1);
				break;
			case BGE_RESET_STOP:
				bge_writemem_ind(sc, BGE_SDI_STATUS, 0x2);
				break;
		}
	}
	
	if (BGE_IS_5705_PLUS(sc))
		sc->bge_return_ring_cnt = BGE_RETURN_RING_CNT_5705;
	else
		sc->bge_return_ring_cnt = BGE_RETURN_RING_CNT;
}

void OSXBGE::bge_sig_post_reset(struct bge_softc *sc, int type)
{	
	if(sc->bge_asf_mode & ASF_NEW_HANDSHAKE)
	{
		switch (type)
		{
			case BGE_RESET_START:
				bge_writemem_ind(sc, BGE_SDI_STATUS, 0x80000001);
				break;
			case BGE_RESET_STOP:
				bge_writemem_ind(sc, BGE_SDI_STATUS, 0x80000002);
				break;
		}
	}
}

void OSXBGE::bge_sig_legacy(struct bge_softc *sc, int type)
{
	if(sc->bge_asf_mode)
	{
		switch (type)
		{
			case BGE_RESET_START:
				bge_writemem_ind(sc, BGE_SDI_STATUS, 0x1);
				break;
			case BGE_RESET_STOP:
				bge_writemem_ind(sc, BGE_SDI_STATUS, 0x2);
				break;
		}
	}
}